{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "public static void println(Object o){\n",
    "    System.out.println(o);\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 生成随机字符/字符串\n",
    "\n",
    "通过下面的示例，演示\n",
    "- 过程式\n",
    "- 面向对象\n",
    "- 设计模式：组合模式\n",
    "- 函数式\n",
    "\n",
    "## 1. 过程式\n",
    "RdnChar中包含了多个生成随机的字符的方法，这些方法都是static的，没有采用面向对象的抽象方式。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "import java.util.Random;\n",
    "\n",
    "public class RdnChar {\n",
    "  // 随机生成一个ch1和ch2之间的字符\n",
    "  public static char getRandomCharacter(char ch1, char ch2) {\n",
    "    return (char) (ch1 + Math.random() * (ch2 - ch1 + 1));\n",
    "  }\n",
    "\n",
    "  /** 随机生成一个小写字符 */\n",
    "  public static char getRandomLowerCaseLetter() {\n",
    "    return getRandomCharacter('a', 'z');\n",
    "  }\n",
    "\n",
    "  // 随机生成一个大写字母\n",
    "  public static char getRandomUpperCaseLetter() {\n",
    "    return getRandomCharacter('A', 'Z');\n",
    "  }\n",
    "\n",
    "  // 随机生成一个数字字符\n",
    "  public static char getRandomDigitCharacter() {\n",
    "    return getRandomCharacter('0', '9');\n",
    "  }\n",
    "\n",
    "  // 随机生成一个字符\n",
    "  public static char getRandomCharacter() {\n",
    "    return getRandomCharacter('\\u0000', '\\uFFFF');\n",
    "  }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面是几个测试用例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "l\n",
      "s\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "import static java.lang.System.*;\n",
    "out.println(\n",
    "    RdnChar.getRandomCharacter('a', 'z')\n",
    ");\n",
    "out.println(\n",
    "    RdnChar.getRandomLowerCaseLetter()\n",
    ");\n",
    "out.println(\n",
    "    RdnChar.getRandomDigitCharacter()\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "在此基础上，如果要加上下面需求：\n",
    "\n",
    "1. 如何产生一个合法的标识符中的字符，比如：^[a-zA-Z0-9]\n",
    "\n",
    "可以以如下的方式实现。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "  /** 产生一个含有多个条件限制的序列，比如说，包含大小写字母和数字的序列 */\n",
    "char getRandomCharacter(char ranges[][]) {\n",
    "    // 随机选择一个约束条件\n",
    "    int index = new Random().nextInt(ranges.length);\n",
    "    return RdnChar.getRandomCharacter(ranges[index][0], ranges[index][1]);\n",
    "}\n",
    "\n",
    "char[][] ranges = {{'0','9'},{'A','Z'},{'a','z'}};\n",
    "getRandomCharacter(ranges);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "问题：\n",
    "\n",
    "上面的方法中，参数`char ranges[][]`是一种不准确的抽象。\n",
    "\n",
    "另外，如果要通过指定的规则，产生一个的字符串，比如：由随机大写字母构成的字符串，使用过程式的方法可能会遇到同样的问题。\n",
    "\n",
    "## 2. 面向对象的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "public class RangedCharGen {\n",
    "\n",
    "    private char from, to;\n",
    "\n",
    "    public RangedCharGen(char from, char to) {\n",
    "        this.from = from;\n",
    "        this.to = to;\n",
    "    }\n",
    "\n",
    "    public char nextChar() {\n",
    "        Random random = new Random();\n",
    "        return (char) (from + random.nextInt(to - from + 1));\n",
    "    }\n",
    "\n",
    "\n",
    "    // 生成一个满足指定规则的字符串\n",
    "    public String nextString(int length) {\n",
    "        StringBuilder sb = new StringBuilder();\n",
    "        for (int i = 0; i < length; i++) {\n",
    "            sb.append(nextChar());\n",
    "        }\n",
    "        return sb.toString();\n",
    "    }\n",
    "}\n",
    "final public class RangedCharGens{\n",
    "    public static final RangedCharGen digit = new RangedCharGen('0', '9');\n",
    "    public static final RangedCharGen lowercase = new RangedCharGen('a', 'z');\n",
    "    public static final RangedCharGen uppercase = new RangedCharGen('A', 'Z');\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "x\n",
      "G\n",
      "oubhupjhnz\n"
     ]
    }
   ],
   "source": [
    "println(RangedCharGens.digit.nextChar());\n",
    "println(RangedCharGens.lowercase.nextChar());\n",
    "println(RangedCharGens.uppercase.nextChar());  \n",
    "println(RangedCharGens.lowercase.nextString(10));  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于引入了RangedCharGen这个概念，产生一个由多个规则约束的字符，比如，生成一个字符，可以是数字，大写字符或小写字符，可以通过以下方式实现："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "S"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "final public class RangedCharGens{\n",
    "    public static final RangedCharGen digit = new RangedCharGen('0', '9');\n",
    "    public static final RangedCharGen lowercase = new RangedCharGen('a', 'z');\n",
    "    public static final RangedCharGen uppercase = new RangedCharGen('A', 'Z');\n",
    "\n",
    "    // 产生一个含有多个条件限制的字符，比如说，包含大小写字母和数字(nextChar)\n",
    "    public static char nextChar(RangedCharGen[] charTypes) {\n",
    "        Random random = new Random();\n",
    "        int index = random.nextInt(charTypes.length);\n",
    "        return charTypes[index].nextChar();\n",
    "    }\n",
    "}\n",
    "\n",
    "RangedCharGens.nextChar(new RangedCharGen[]{\n",
    "    RangedCharGens.digit, \n",
    "    RangedCharGens.lowercase, \n",
    "    RangedCharGens.uppercase\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "UML图如下\n",
    "\n",
    "![](./images/class.png)\n",
    "\n",
    "考虑进一步的需求：\n",
    "如何生成包括指定'_','$'等特殊字符在内的字符。 这并不能通过RangedCharGen来实现。\n",
    "\n",
    "此时需要引入接口\n",
    "\n",
    "## 3. 基于组合模式的实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "interface CharGen {\n",
    "    char nextChar();    \n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "目前这个接口提供了下面三种实现方式:\n",
    "1. 基于一个范围的随机字符生成\n",
    "2. 基于一个字符集的随机字符生成\n",
    "3. 基于多种CharGen的组合生成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "public class RangedCharGen implements CharGen {\n",
    "    final private char from, to;\n",
    "    public RangedCharGen(char from, char to) {\n",
    "        this.from = from;\n",
    "        this.to = to;\n",
    "    }\n",
    "    public char nextChar() {\n",
    "        return (char) (from + Math.random() * (to - from + 1)); \n",
    "    }\n",
    "}\n",
    "\n",
    "public class EnumCharGen implements CharGen {\n",
    "    final private char[] chars;\n",
    "    public EnumCharGen(char... chars) {\n",
    "        this.chars = chars;\n",
    "    }\n",
    "    public char nextChar() {\n",
    "        return chars[(int) (Math.random() * chars.length)];\n",
    "    } \n",
    "}\n",
    "\n",
    "public class CompositeCharGen implements CharGen {\n",
    "    final private CharGen[] charGens;\n",
    "    public CompositeCharGen(CharGen... charGens) {\n",
    "        this.charGens = charGens;\n",
    "    }\n",
    "    public char nextChar() {\n",
    "        return charGens[(int) (Math.random() * charGens.length)].nextChar();\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "预定义一些常量以及默认的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [],
   "source": [
    "public class CharGens{\n",
    "\n",
    "    public static CharGen lowercase = new RangedCharGen('a', 'z');\n",
    "    public static CharGen uppercase = new RangedCharGen('A', 'Z');\n",
    "    public static CharGen digit = new RangedCharGen('0', '9');\n",
    "    public static CharGen alpha = new CompositeCharGen( lowercase, uppercase );\n",
    "    public static CharGen alphaNumeric = new CompositeCharGen( alpha, digit );\n",
    "\n",
    "}\n",
    "\n",
    "interface CharGen{\n",
    "    char nextChar();\n",
    "    default String nextString(int length){\n",
    "        String str = \"\";\n",
    "        for (int i = 0; i < length; i++) {\n",
    "            str += nextChar();\n",
    "        }\n",
    "        return str;\n",
    "    }    \n",
    "}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用组合模式的生成字符和字符串。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "vscode": {
     "languageId": "java"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "u\n",
      "3854030830\n",
      "QHGacVEHJzvtdUkOVZsC\n",
      "2A5_$3$$1_5$5_W4P9_2\n"
     ]
    }
   ],
   "source": [
    "// 生成一个小写字符\n",
    "System.out.println(CharGens.lowercase.nextChar());\n",
    "// 生成一个长度为10的数字字符串\n",
    "System.out.println(CharGens.digit.nextString(10));\n",
    "// 生成一个长度为20的随机含数字和字母的字符串\n",
    "System.out.println(CharGens.alpha.nextString(20));\n",
    "System.out.println(new CompositeCharGen(\n",
    "    CharGens.digit, \n",
    "    CharGens.alpha,\n",
    "    new EnumCharGen('_', '$')\n",
    ").nextString(20));\n",
    "// 生成一个长度为30的随机含数字和字母的字符串"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "UML图如下：\n",
    "![](./images/composite.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 函数式\n",
    "\n",
    "下面是一个基于Scala的函数式的实现，可以看出，的函数式编程非常灵活，而且代码非常简洁。\n",
    "```scala\n",
    "type CharGen = () => Char\n",
    "\n",
    "def rangedCharGen(start:Char, end: Char): CharGen  = \n",
    "  () => (Math.abs(Random.nextInt) % (end - start) + start).toChar\n",
    "val lowercaseGen:CharGen =  rangedCharGen('a','z')\n",
    "val uppercaseGen:CharGen = rangedCharGen('A','Z')\n",
    "val digitGen:CharGen =  rangedCharGen('0','9')\n",
    "\n",
    "def enumGen(chars: Char*):CharGen = \n",
    "  () => chars(Random.nextInt(chars.length))\n",
    "def compositeGen(gens: CharGen*) : CharGen =\n",
    "  () => gens(Random.nextInt(gens.length))()\n",
    "  \n",
    "def string(gen: CharGen, len: Int) = List.fill(len)(gen()).mkString\n",
    "\n",
    "@main\n",
    "def main = {\n",
    "  println(string(lowercaseGen, 10))\n",
    "  println(string(enumGen('a','b','c'), 10))\n",
    "  println(string(compositeGen(lowercaseGen, uppercaseGen, enumGen('$','_')), 10))\n",
    "}\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Java",
   "language": "java",
   "name": "java"
  },
  "language_info": {
   "codemirror_mode": "java",
   "file_extension": ".jshell",
   "mimetype": "text/x-java-source",
   "name": "Java",
   "pygments_lexer": "java",
   "version": "11.0.21+9-LTS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
